<template>
  <div>
    <ElTabs
      class="skeleton-nav-tabs page-tabs"
      :style="collapse ? 'left: 64px' : ''"
      :value="currentPath"
      type="border-card"
      @edit="removeTab"
      @tab-click="onTabClick">
      <ElTabPane
        v-for="(item, index) in tabs"
        :key="item.path"
        :closable="item.path !== '/'"
        :label="item.title"
        :name="item.path">
        <template #label>
          <ElTooltip :content="item.title" :open-delay="800">
            <!--使用上下文菜单-->
            <p v-if="contextMenu" class="tab-label" @contextmenu.prevent="onContextMenu($event, item, index)">
              <i :class="item.icon || 'el-icon-tickets'" class="tab-icon"></i>{{ item.title }}
            </p>
            <!--不使用上下文菜单-->
            <p v-else class="tab-label">
              <i :class="item.icon || 'el-icon-tickets'" class="tab-icon"></i>{{ item.title }}
            </p>
          </ElTooltip>
        </template>
      </ElTabPane>
    </ElTabs>
    <div v-if="contextMenu" v-show="showMenu" ref="contextMenu" :style="{ top, left }" class="context-menu">
      <ul>
        <li
          v-for="(item, index) in menus"
          v-show="item.show"
          :key="index"
          class="context-menu-item"
          @click="onMenuItemClick(item, index)"
          @mousedown.stop>
          {{ item.label }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
  import { mapMutations, mapState } from 'vuex'
  import { maxBy } from 'lodash-es'
  import { cacheManager } from './index'
  import { hasOwn } from '@web-utils/core'

  export default {
    name: 'SkeletonNavTabs',
    props: {
      contextMenu: Boolean,
      collapse: Boolean,
      lru: {
        type: Boolean,
        default: true
      }
    },
    bus: {
      closeTab(path) {
        this.removeTab(path, 'remove')
      }
    },
    data() {
      return {
        top: '0px',
        left: '0px',
        showMenu: false,
        menus: [
          {
            label: '关闭',
            show: true,
            action: 'closeCurTab'
          },
          {
            label: '关闭左侧',
            show: true,
            action: 'closeLeftTabs'
          },
          {
            label: '关闭右侧',
            show: true,
            action: 'closeRightTabs'
          },
          {
            label: '关闭其他',
            show: true,
            action: 'closeOtherTabs'
          },
          {
            label: '关闭全部',
            show: true,
            action: 'closeAllTabs'
          }
        ],
        currentTab: null
      }
    },
    computed: {
      ...mapState('viewState', ['tabs']),
      currentPath() {
        return this.$route.meta.title ? this.$route.path : this.$route.matched[1]?.path
      }
    },
    watch: {
      $route: 'addTab'
    },
    created() {
      this.addTab()
    },
    mounted() {
      if (this.contextMenu) {
        document.addEventListener('mousedown', this.hideMenu)
        this.$once('hook:beforeDestroy', () => {
          document.removeEventListener('mousedown', this.hideMenu)
        })
      }
    },
    methods: {
      ...mapMutations('viewState', ['addTabByRoute', 'removeTabByPath', 'setTabs']),
      async removeTab(path, action) {
        try {
          if (action === 'remove') {
            const tab = this.tabs.find(value => value.path === path)

            const view = cacheManager.get(tab.path)
            if (view && hasOwn(view.componentInstance, 'isFormSaved') && !view.componentInstance.isFormSaved) {
              await this.$confirm('表单尚未保存, 是否关闭?', '提示', { type: 'warning' })
            }

            this.removeTabByPath(path)
            let item
            if (this.lru) {
              item = this.findLatestTab(this.tabs)
            } else {
              item = this.tabs[this.tabs.length - 1]
            }
            if (this.currentPath === path) {
              await this.$router.push(item.fullPath)
            }
            this.removeCache(tab)
          }
        } catch (e) {}
      },
      removeCache(tab) {
        if (tab?.path) {
          cacheManager.remove(tab.path)
        }
      },
      addTab() {
        this.addTabByRoute(this.$route)
      },
      async onTabClick(tab) {
        const index = this.tabs.findIndex(value => value.path === tab.name)
        index > -1 && (await this.$router.push(this.tabs[index].fullPath))
      },
      focusTabItem(event) {
        let tabItem = event.target.parentNode
        while (tabItem && !tabItem.classList.contains('el-tabs__item')) {
          tabItem = tabItem.parentNode
        }
        if (tabItem) {
          tabItem.focus()
        }
      },
      async onContextMenu(event, tab, index) {
        if (this.tabs.length === 1) {
          return
        }
        this.focusTabItem(event)

        this.menus[0].show = tab.path !== '/'
        this.menus[1].show = index !== 0 && index !== 1
        this.menus[2].show = index < this.tabs.length - 1
        this.menus[3].show = this.tabs.length > 2

        this.showMenu = true

        await this.$nextTick()
        const bodyWidth = window.document.body.offsetWidth
        const menuWidth = this.$refs.contextMenu.offsetWidth

        let left = event.clientX + 2
        const top = event.clientY + 2

        if (left + menuWidth > bodyWidth) {
          left = bodyWidth - menuWidth - 4
        }

        this.left = `${left}px`
        this.top = `${top}px`

        this.index = index
        this.currentTab = tab
      },
      hideMenu() {
        this.showMenu = false
        this.currentTab = {}
        this.index = -1
      },
      async onMenuItemClick(item, index) {
        await this[item.action](item, index)
        this.hideMenu()
      },
      async closeCurTab() {
        const currentTab = this.currentTab
        this.hideMenu()
        await this.removeTab(currentTab.path, 'remove')
      },
      async batchClose(filter) {
        const currentTab = this.currentTab
        this.hideMenu()
        const tabs = this.tabs.filter(filter)
        const closeTabs = this.tabs.filter((item, index) => !filter(item, index))
        const removedTabs = []
        for (let i = 0; i < closeTabs.length; i++) {
          const tab = closeTabs[i]
          const view = cacheManager.get(tab.path)
          if (view && hasOwn(view.componentInstance, 'isFormSaved') && !view.componentInstance.isFormSaved) {
            await this.$router.push(tab.fullPath)
            try {
              await this.$confirm('表单尚未保存, 是否关闭?', '提示', { type: 'warning' })
              this.removeCache(tab)
              removedTabs.push(tab)
              const restTabs = closeTabs.filter(value => !removedTabs.includes(value))
              const unsavedFormTab = restTabs.find(value => {
                const vc = cacheManager.get(value.path)
                return vc && hasOwn(vc.componentInstance, 'isFormSaved') && !vc.componentInstance.isFormSaved
              })
              if (unsavedFormTab) {
                await this.$router.push(unsavedFormTab.fullPath)
              } else {
                if (!removedTabs.includes(currentTab)) {
                  await this.$router.push(currentTab.fullPath)
                } else {
                  let item
                  if (tabs.length === 1) {
                    item = tabs[0]
                  } else {
                    item = maxBy(tabs, 'time')
                  }
                  await this.$router.push(item.fullPath)
                }
              }
            } catch (e) {
              break
            }
          } else {
            removedTabs.push(tab)
            this.removeCache(tab)
          }
        }
        const reserveTabs = closeTabs.filter(value => !removedTabs.includes(value))
        if (!removedTabs.includes(currentTab) && !tabs.includes(currentTab)) {
          tabs.push(currentTab)
        }
        tabs.push(...reserveTabs.filter(value => !tabs.includes(value)))
        tabs.sort((a, b) => this.tabs.findIndex(value => value === a) - this.tabs.findIndex(value => value === b))
        this.setTabs(tabs)
        if (!tabs.find(value => value.path === this.currentPath)) {
          const item = this.findLatestTab(tabs)
          await this.$router.push(item.fullPath)
        }
      },
      findLatestTab(tabs) {
        let tab
        if (tabs.length === 1) {
          tab = tabs[0]
        } else {
          tab = maxBy(tabs, 'time')
        }
        return tab
      },
      async closeOtherTabs() {
        const currentTab = this.currentTab
        await this.batchClose(item => item.path === '/' || item.path === currentTab.path)
      },
      async closeAllTabs() {
        await this.batchClose(item => item.path === '/')
      },
      async closeRightTabs() {
        const currentIndex = this.index + 1
        await this.batchClose((item, index) => item.path === '/' || index < currentIndex)
      },
      async closeLeftTabs() {
        const currentIndex = this.index - 1
        await this.batchClose((item, index) => item.path === '/' || index > currentIndex)
      }
    }
  }
</script>

<style lang="scss">
  .page-tabs {
    &.el-tabs.skeleton-nav-tabs {
      height: 40px;
      margin-left: 0;
      padding-left: 0;
      border-bottom: 1px solid #eff2f7;

      .el-tabs__header {
        height: 40px;
        background: #fff;
        border-bottom: 2px solid var(--color-primary);
      }

      .el-tabs__item {
        height: 36px;
        margin: 4px 0 0 5px;
        color: #7f7f7f;
        line-height: 36px;
        background: #f3f3f3;
        border: 1px #aaa solid;
        border-bottom: 2px var(--color-primary) solid;
        border-radius: 4px 4px 0 0;
        box-shadow: rgb(201, 197, 197) 0 0 5px;

        .el-icon-close {
          top: 11px;
          visibility: hidden;

          &:hover {
            background: #f56c6c;
          }
        }

        &:hover {
          .el-icon-close {
            visibility: visible;
          }
        }
      }

      .el-tabs__item:not(.is-disabled).is-active,
      .skeleton-nav-tabs.el-tabs .el-tabs__item:not(.is-disabled):hover,
      .skeleton-nav-tabs.el-tabs .el-tabs__item:not(.is-disabled):focus {
        color: var(--color-primary);
        background: #fff;
        border: 1px var(--color-primary) solid;
        border-bottom-color: #fff;
        border-radius: 4px 4px 0 0;
      }
    }
  }
</style>
